#!/usr/bin/env python
# encoding:utf-8
'''
@author: yangyongjie
@license: (C) Copyright 2013-2017, Node Supply Chain Manager Corporation Limited. 
@contact: deamoncao100@gmail.com
software: garner
@file: auth.py
@time: 2022/8/25 15:16
@desc:
'''
import time
from flask import request, current_app
from libs.response import generate_response
from functools import wraps
from hashlib import md5
from model.user import ApiToken
from itsdangerous import TimedJSONWebSignatureSerializer as TJS
from itsdangerous import BadSignature, SignatureExpired
from libs.error_code import TokenFailException

#自定义权限认证装饰器
def login(func):
    @wraps(func)   #保留装饰的函数元数据
    def inner(*args, **kwargs):
        token = request.args.get("token")
        if token and verify_token(token):
            uid = verify_token(token)
            print(f"token is {uid}")
            result = func(*args, **kwargs)
            return result
        elif not token and api_auth():
            result = func(*args, **kwargs)
            return result
        else:
            return generate_response(status_code=10002, message="auth fail")
    return inner

def api_auth():
    params = request.args #客户端传递过来url的参数
    appid = params.get("appid")
    salt = params.get("salt")  # 盐值
    sign = params.get("sign")  # 签名
    timestamp = params.get("timestamp")  # 时间戳

    if time.time() - int(timestamp) > 600:
        return False

    api_token = ApiToken.query.filter_by(appid=appid).first()
    if not api_token:
        return False

    # 验证有没有此url和方法的权限
    if not has_permission(api_token, request.path, request.method.lower()):
        return False
    # 获取数据库里的密钥
    secretkey = api_token.secretkey
    # 生成服务端的签名
    # 可以加上时间戳来防止签名被别人盗取，重复访问
    user_sign = appid + salt + secretkey + timestamp
    m1 = md5()
    m1.update(user_sign.encode(encoding="utf-8"))
    # 判断客户端传递过来的签名和服务端生成签名是否一致
    if sign != m1.hexdigest():
        # raise AuthFailException
        return False
    else:
        return True


# url验证
# 192.168.2.152:9000/stuapi  GET
def has_permission(api_token, url, method):
    # 客户端请求的方法和url
    # get/stuapi
    mypermission = method + url
    # 获取此api_token对象的所有url权限
    all_permission = [permission.method_type + permission.url
                      for permission in api_token.manage]
    if mypermission in all_permission:
        return True
    else:
        return False


# 签名认证 -- api授权
#1.客户端向服务器请求appid和secretkey
#2.服务端提供appid和setretkey，以及相应的算法
#  客户端拿到信息之后根据相应的算法生成签名，发给服务端
#3.服务端比较签名是否一致

#https
#加时间戳
#JWT JWS JWE
#https://www.jianshu.com/p/50ade6f2e4fd
#生成token
def create_token(uid):
    #第一个参数传递内部私钥,测试环境写死
    #第二个参数是有效期
    s = TJS(current_app.config["SECRET_KEY"], expires_in=current_app.config["EXPIRES_IN"])
    #s = TJS("123456", expires_in=10)
    token = s.dumps({"uid":uid}).decode("ascii")
    return token

#每个内容之间用.分割
#header:   base64enc({"alg":"HS512","iat":1661307558,"exp":1661307618})
#playload:  base64enc({"uid":15})
#signature: sha512(base64enc(header)+.+base64enc(playload)+serectkey)


def verify_token(token):
    s = TJS(current_app.config["SECRET_KEY"])
    try:
        data = s.loads(token)
    except BadSignature:
        print("xxxxxxxxxxxxxxxx")
        raise TokenFailException
        # raise RuntimeError("签名错误")
    except SignatureExpired:
        raise RuntimeError("token过期")
    return data
